package com.youlin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.youlin.security.SecurityUser;
import com.youlin.dto.*;
import com.youlin.dto.req.AddAdminReqDto;
import com.youlin.dto.res.AdminResDto;
import com.youlin.dto.res.EditAdminResDto;
import com.youlin.dto.res.RoleResDto;
import com.youlin.entities.BygAdmin;
import com.youlin.entities.BygAppointment;
import com.youlin.entities.BygRole;
import com.youlin.entities.BygUnit;
import com.youlin.mapper.BygUnitAdminMapper;
import com.youlin.security.UnitNotFoundException;
import com.youlin.service.BygAdminService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlin.service.BygAppointmentService;
import com.youlin.service.BygRoleService;
import com.youlin.service.BygUnitService;
import com.youlin.untils.*;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@Service
public class BygAdminServiceImpl extends ServiceImpl<BygUnitAdminMapper, BygAdmin> implements BygAdminService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private BygUnitService bygUnitService;
    @Autowired
    private BygRoleService bygRoleService;
    @Autowired
    private BygAppointmentService bygAppointmentService;
    @Autowired
    private SessionRegistry sessionRegistry;

    @Override
    public UserDetails loadUserByUsername(String code_name) throws UsernameNotFoundException {
        if (StringUtils.isBlank(code_name)) throw new UsernameNotFoundException("登录账号错误");
        String[] arr = code_name.split("_");
        if (arr.length != 2) throw new UsernameNotFoundException("登录账号错误");
        if (StringUtils.isBlank(arr[0]) || StringUtils.isBlank(arr[1]))
            throw new UsernameNotFoundException("登录账号错误");
        BygUnit bygUnit = bygUnitService.getOne(new QueryWrapper<BygUnit>().eq("code", arr[0]));
        if (bygUnit == null) throw new UnitNotFoundException("单位编号错误");
        BygAdmin admin = getOne(new QueryWrapper<BygAdmin>().eq("unit_id", bygUnit.getId()).eq("name", arr[1]));
        if (admin == null) throw new UsernameNotFoundException("登录账号错误");
        BygRole role = bygRoleService.getById(admin.getRoleId());
        return new SecurityUser(admin.setRole(role));
    }

    @Override
    public PageDataResDto<List<AdminResDto>> findAllAdmins(BygAdmin currentAdmin, PageInfo pageInfo) {
        QueryWrapper<BygAdmin> queryWrapper = new QueryWrapper<BygAdmin>()
                .eq("unit_id", currentAdmin.getUnitId())
                .in("role_id", Arrays.asList(MyEnum.ROLE_OPERATOR_ID.getValue(), MyEnum.ROLE_REVIEWER_ID.getValue()))
                .orderByDesc("create_time");
        IPage<BygAdmin> page = page(new Page<>(pageInfo.getPage(), pageInfo.getSize()), queryWrapper);
        List<AdminResDto> bygAdmins = BaseService.findAndDealWithOneToOne(page.getRecords(), BygAdmin::getRoleId,
                AdminResDto.class, AdminResDto::setRole, bygRoleService, BygRole::getId, RoleResDto.class);
        pageInfo = new PageInfo(page.getCurrent(), page.getSize(), page.getTotal());
        return new PageDataResDto<List<AdminResDto>>(Status.SUCCESS).setData(bygAdmins).setPageInfo(pageInfo);
    }

    @Override
    @Transactional
    public BaseResDto updPwd(Integer adminId, String oldPwd, String newPwd) {
        if (! newPwd.matches("^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z\\d]{8,20}$"))
            return new BaseResDto(Status.NEWPWDERROR);
        BygAdmin admin = getById(adminId).setReset(false);
        if (admin == null) return new BaseResDto(Status.PARAMETERERROR);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (! encoder.matches(oldPwd, admin.getPwd()))
            return new BaseResDto(Status.OLDPWDERROR);
        if (! updateById(admin.setPwd(encoder.encode(newPwd))))
            return new BaseResDto(Status.ERROR);
        BygRole role = bygRoleService.getById(admin.getRoleId());
        AdminUtil.setCurrentAdmin(new SecurityUser(admin.setRole(role)));
        return new BaseResDto(Status.SUCCESS);
    }

    @Override
    public BaseDataResDto<String> getCaptchaBase64() {
        String captchaBase64;
        try {
            captchaBase64 = CaptchaUtil.getCaptchaBase64(redisTemplate);
        } catch (IOException e) {
            return new BaseDataResDto<>(Status.ERROR);
        }
        if (StringUtils.isBlank(captchaBase64)) return new BaseDataResDto<>(Status.ERROR);
        return new BaseDataResDto<String>(Status.SUCCESS).setData(captchaBase64);
    }

    @Override
    public BaseResDto validateCaptcha(String code) {
        if (! CaptchaUtil.validateCaptcha(redisTemplate, code))
            return new BaseResDto(Status.ERROR);
        redisTemplate.delete("captcha:"+code);
        return new BaseResDto(Status.SUCCESS);
    }

    @Override
    @Transactional
    public BaseResDto addAdmin(Integer unitId, AddAdminReqDto data) {
        List<BygAdmin> bygAdmins = list(new QueryWrapper<BygAdmin>().eq("unit_id", unitId));
        if (bygAdmins.parallelStream().anyMatch(a -> a.getName().equals(data.getName())))
            return new BaseResDto(Status.NAMEDUPLICATE);
        if (bygAdmins.parallelStream().anyMatch(a -> a.getNick().equals(data.getNick())))
            return new BaseResDto(Status.NICKDUPLICATE);
        String pwd = new BCryptPasswordEncoder().encode("123456");
        BygAdmin admin = MyBeanUtil.copyProperties(data, BygAdmin.class);
        admin.setUnitId(unitId).setPwd(pwd).setCreateTime(LocalDateTime.now());
        if (save(admin)) return new BaseResDto(Status.SUCCESS);
        return new BaseResDto(Status.ERROR);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResDto editAdmin(Integer unitId, EditAdminResDto data) {
        BygAdmin admin;
        if (data.getId() == null) {
            List<BygAdmin> bygAdmins = list(new QueryWrapper<BygAdmin>().eq("unit_id", unitId));
            if (bygAdmins.parallelStream().anyMatch(a -> a.getName().equals(data.getName())))
                return new BaseResDto(Status.NAMEDUPLICATE);
            if (bygAdmins.parallelStream().anyMatch(a -> a.getNick().equals(data.getNick())))
                return new BaseResDto(Status.NICKDUPLICATE);
            String pwd = new BCryptPasswordEncoder().encode("123456");
            admin = MyBeanUtil.copyProperties(data, BygAdmin.class)
                    .setUnitId(unitId).setPwd(pwd).setCreateTime(LocalDateTime.now());
        } else {
            admin = new BygAdmin().setId(data.getId()).setTel(data.getTel());
            if (StringUtils.isNotBlank(data.getNick())) {
                QueryWrapper<BygAdmin> queryWrapper = new QueryWrapper<BygAdmin>().eq("unit_id", unitId)
                        .ne("id", data.getId()).eq("nick", data.getNick());
                if (getOne(queryWrapper) != null) return new BaseResDto(Status.NICKDUPLICATE);
                admin.setNick(data.getNick());
            }
            if (BooleanUtils.isTrue(data.getResetPwd()))
                admin.setPwd(new BCryptPasswordEncoder().encode("123456")).setReset(true);
        }
        if (! saveOrUpdate(admin)) return new BaseResDto(Status.ERROR);
        if (BooleanUtils.isTrue(data.getResetPwd()))
            AdminUtil.removeUser(sessionRegistry, admin.getId());
        return new BaseResDto(Status.SUCCESS);
    }

    @Override
    @Transactional
    public BaseResDto upDownAdmin(Integer adminId) {
        BygAdmin admin = getById(adminId);
        if (! updateById(admin.setUsable(! admin.getUsable())))
            return new BaseResDto(Status.ERROR);
        return new BaseResDto(Status.SUCCESS);
    }

    @Override
    public BaseResDto checkAdmin(Integer adminId) {
        QueryWrapper<BygAppointment> queryWrapper = new QueryWrapper<BygAppointment>()
                .nested(qw -> qw.eq("operator_id", adminId)
                        .in("status", Arrays.asList(MyEnum.AM_CREATE.getValue(), MyEnum.AM_OPERATE.getValue())))
                .or(qw -> qw.eq("reviewer_id", adminId).eq("status", MyEnum.AM_REVIEWING.getValue()));
        List<BygAppointment> appointments = bygAppointmentService.list(queryWrapper);
        if (appointments.size() > 0) return new BaseResDto(Status.UNDONEWORK);
        return new BaseResDto(Status.SUCCESS);
    }

}
